using System;
using System.Collections.Generic;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// Represents a mathematical fraction with a numerator and an denominator.
    /// </summary>
    [Serializable]
    public class Fraction : IFraction
    {
        /// <summary>
        /// Represents the numerator of the fraction
        /// </summary>
        private long numerator;

        /// <summary>
        /// Represents the denominator of the fraction
        /// </summary>
        private long denominator;

        /// <summary>
        /// Initializes a new instance of the <see cref="Fraction"/> class.
        /// </summary>
        public Fraction()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Fraction"/> class.
        /// </summary>
        /// <param name="numerator">The numerator of the fraction.</param>
        /// <param name="denominator">The denominator of the fraction.</param>
        public Fraction(long numerator, long denominator)
        {
            this.numerator = numerator;
            this.denominator = denominator;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Fraction"/> class.
        /// </summary>
        /// <param name="number">The decimal number. This number will be transformed to a fraction.</param>
        public Fraction(double number)
        {
            Decimal tempuri = new Decimal(number);
            Decimal d = tempuri - Decimal.Truncate(tempuri);

            if (d < 0)
            {
                d = -d;
            }

            double t1 = d.ToString().Length - 2;
            Decimal factor = new Decimal(Math.Pow(10, (d.ToString().Length - 2)));

            this.numerator = (long) (tempuri * factor);
            this.denominator = (long) factor;
        }

        /// <summary>
        /// Gets or sets the numerator.
        /// </summary>
        /// <value>The numerator.</value>
        public long Numerator
        {
            get { return numerator; }
            set { numerator = value; }
        }

        /// <summary>
        /// Gets or sets the denominator.
        /// </summary>
        /// <value>The denominator.</value>
        public long Denominator
        {
            get { return denominator; }
            set { denominator = value; }
        }

        /// <summary>
        /// Swaps the components (numerator and denominator).
        /// </summary>
        public void SwapComponents()
        {
            long tempuri = this.numerator;

            this.numerator = this.denominator;
            this.denominator = tempuri;
        }

        /// <summary>
        /// Cancels this fraction to its absolute minimum.
        /// </summary>
        public void Cancel()
        {
            long ggt = GetGCD(this.numerator, this.denominator);

            while (ggt > 1)
            {
                this.numerator /= ggt;
                this.denominator /= ggt;
                ggt = GetGCD(this.numerator, this.denominator);
            }
        }

        /// <summary>
        /// Searches the greatest common divisor of two numbers.
        /// </summary>
        /// <param name="number1">The first number.</param>
        /// <param name="number2">The second number.</param>
        /// <returns>The greatest common divisor.</returns>
        private static long GetGCD(long number1, long number2)
        {
            long remainder = 0;

            do
            {
                remainder = number1 % number2;
                number1 = number2;
                number2 = remainder;
            } while (remainder != 0);

            return number1;
        }

        /// <summary>
        /// Copies this instance.
        /// </summary>
        /// <returns>Returns a copy of the current instance.</returns>
        public Fraction Copy()
        {
            return this;
        }

        /// <summary>
        /// Raises the specified power to the fraction.
        /// </summary>
        /// <param name="power">The specified power.</param>
        public void Pow(int power)
        {
            this.Pow((double) power);
        }

        /// <summary>
        /// Raises the specified power to the fraction.
        /// </summary>
        /// <param name="power">The specified power.</param>
        public void Pow(double power)
        {
            Fraction tempuri = new Fraction(Math.Pow(this.ToDouble(), power));

            this.numerator = tempuri.Numerator;
            this.denominator = tempuri.Denominator;
        }

        /// <summary>
        /// Raises the square root to the fraction.
        /// </summary>
        public void Sqrt()
        {
            Fraction tempuri = new Fraction(Math.Sqrt(this.ToDouble()));

            this.numerator = tempuri.Numerator;
            this.denominator = tempuri.Denominator;
        }

        /// <summary>
        /// Raises the specified root to the fraction.
        /// </summary>
        /// <param name="root">The specified root.</param>
        public void Root(int root)
        {
            this.Root((double) root);
        }

        /// <summary>
        /// Raises the specified root to the fraction.
        /// </summary>
        /// <param name="root">The specified root.</param>
        public void Root(double root)
        {
            double t = Math.Pow(this.ToDouble(), 1 / root);
            Fraction tempuri = new Fraction(Math.Pow(this.ToDouble(), 1 / root));

            this.numerator = tempuri.Numerator;
            this.denominator = tempuri.Denominator;
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static Fraction operator +(Fraction a, Fraction b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            long newDenominator = a.Denominator * b.Denominator;

            a.Numerator *= b.Denominator;
            b.Numerator *= a.Denominator;

            a.Denominator = newDenominator;
            b.Denominator = newDenominator;

            Fraction result = new Fraction();

            result.Numerator = a.Numerator + b.Numerator;
            result.Denominator = a.Denominator; // +b.Denominator;

            return result;
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="a">The first number.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static Fraction operator +(double a, Fraction b)
        {
            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            return new Fraction(a) + b;
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second number.</param>
        /// <returns>The result of the operator.</returns>
        public static Fraction operator +(Fraction a, double b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            return a + new Fraction(b);
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static Fraction operator -(Fraction a, Fraction b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            long newDenominator = a.Denominator * b.Denominator;

            a.Numerator *= b.Denominator;
            b.Numerator *= a.Denominator;

            a.Denominator = newDenominator;
            b.Denominator = newDenominator;

            Fraction result = new Fraction();

            result.Numerator = a.Numerator - b.Numerator;
            result.Denominator = a.Denominator; // -b.Denominator;

            return result;
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="a">The first number.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static Fraction operator -(double a, Fraction b)
        {
            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            return new Fraction(a) - b;
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second number.</param>
        /// <returns>The result of the operator.</returns>
        public static Fraction operator -(Fraction a, double b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            return a - new Fraction(b);
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static Fraction operator *(Fraction a, Fraction b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            Fraction result = new Fraction();

            result.Numerator = a.Numerator * b.Numerator;
            result.Denominator = a.Denominator * b.Denominator;

            return result;
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="a">The first number.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static Fraction operator *(double a, Fraction b)
        {
            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            return new Fraction(a) * b;
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second number.</param>
        /// <returns>The result of the operator.</returns>
        public static Fraction operator *(Fraction a, double b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            return a * new Fraction(b);
        }

        /// <summary>
        /// Implements the operator /.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static Fraction operator /(Fraction a, Fraction b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            b.SwapComponents();

            return a * b;
        }

        /// <summary>
        /// Implements the operator /.
        /// </summary>
        /// <param name="a">The first number.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static Fraction operator /(double a, Fraction b)
        {
            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            return new Fraction(a) / b;
        }

        /// <summary>
        /// Implements the operator /.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second number.</param>
        /// <returns>The result of the operator.</returns>
        public static Fraction operator /(Fraction a, double b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            return a / new Fraction(b);
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(Fraction a, Fraction b)
        {
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }

            if (((object) a == null) || ((object) b == null))
            {
                return false;
            }

            return ((a.Numerator == b.Numerator) && (a.Denominator == b.Denominator));
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="a">The first number.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(double a, Fraction b)
        {
            if (((object) a == null) || ((object) b == null))
            {
                return false;
            }

            return new Fraction(a) == b;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second number.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(Fraction a, double b)
        {
            if (((object) a == null) || ((object) b == null))
            {
                return false;
            }

            return a == new Fraction(b);
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(Fraction a, Fraction b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="a">The first number.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(double a, Fraction b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second number.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(Fraction a, double b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Implements the operator &lt;.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator <(Fraction a, Fraction b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            return a.ToDouble() < b.ToDouble();
        }

        /// <summary>
        /// Implements the operator &lt;.
        /// </summary>
        /// <param name="a">The first number.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator <(double a, Fraction b)
        {
            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            return a < b.ToDouble();
        }

        /// <summary>
        /// Implements the operator &lt;.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second number.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator <(Fraction a, double b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            return a.ToDouble() < b;
        }

        /// <summary>
        /// Implements the operator &lt;=.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator <=(Fraction a, Fraction b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            return a.ToDouble() <= b.ToDouble();
        }

        /// <summary>
        /// Implements the operator &lt;=.
        /// </summary>
        /// <param name="a">The first number.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator <=(double a, Fraction b)
        {
            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            return a <= b.ToDouble();
        }

        /// <summary>
        /// Implements the operator &lt;=.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second number.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator <=(Fraction a, double b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            return a.ToDouble() <= b;
        }

        /// <summary>
        /// Implements the operator &gt;.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator >(Fraction a, Fraction b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            return a.ToDouble() > b.ToDouble();
        }

        /// <summary>
        /// Implements the operator &gt;.
        /// </summary>
        /// <param name="a">The first number.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator >(double a, Fraction b)
        {
            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            return a > b.ToDouble();
        }

        /// <summary>
        /// Implements the operator &gt;.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second number.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator >(Fraction a, double b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            return a.ToDouble() > b;
        }

        /// <summary>
        /// Implements the operator &gt;=.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator >=(Fraction a, Fraction b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            return a.ToDouble() >= b.ToDouble();
        }

        /// <summary>
        /// Implements the operator &gt;=.
        /// </summary>
        /// <param name="a">The first number.</param>
        /// <param name="b">The second fraction.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator >=(double a, Fraction b)
        {
            if (b == (Fraction) null)
            {
                throw new ArgumentNullException("b");
            }

            return a >= b.ToDouble();
        }

        /// <summary>
        /// Implements the operator &gt;=.
        /// </summary>
        /// <param name="a">The first fraction.</param>
        /// <param name="b">The second number.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator >=(Fraction a, double b)
        {
            if (a == (Fraction) null)
            {
                throw new ArgumentNullException("a");
            }

            return a.ToDouble() >= b;
        }

        /// <summary>
        /// Generates a random fraction number.
        /// </summary>
        /// <returns>A random fraction number.</returns>
        public static Fraction GetRandomNumber()
        {
            return Fraction.GetRandomNumber(Int32.MaxValue);
        }

        /// <summary>
        /// Generates a random fraction number.
        /// </summary>
        /// <param name="maxValue">The maximum value of generation.</param>
        /// <returns>A random fraction number.</returns>
        public static Fraction GetRandomNumber(int maxValue)
        {
            Random random = new Random();

            return new Fraction(random.Next(maxValue), random.Next(maxValue));
        }

        /// <summary>
        /// Converts the fraction to a decimal number.
        /// </summary>
        /// <returns>The converted decimal number.</returns>
        public Decimal ToDecimal()
        {
            return (Decimal) this.numerator / (Decimal) this.denominator;
        }

        /// <summary>
        /// Converts the fraction to a decimal number.
        /// </summary>
        /// <returns>The converted decimal number.</returns>
        public Double ToDouble()
        {
            return (Double) this.numerator / (Double) this.denominator;
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            return this.numerator.ToString() + "/" + this.denominator.ToString();
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }
    }
}